// package com.gitee.c0dehvb.reactivesamples.reactivesamples.config;
//
// import com.google.common.collect.Lists;
// import io.r2dbc.spi.Connection;
// import io.r2dbc.spi.ConnectionFactory;
// import lombok.extern.slf4j.Slf4j;
// import org.apache.shardingsphere.sql.parser.SQLParserEngine;
// import org.apache.shardingsphere.sql.parser.binder.SQLStatementContextFactory;
// import org.apache.shardingsphere.sql.parser.binder.metadata.schema.SchemaMetaData;
// import org.apache.shardingsphere.sql.parser.binder.statement.SQLStatementContext;
// import org.apache.shardingsphere.sql.parser.sql.statement.SQLStatement;
// import org.apache.shardingsphere.underlying.common.config.DatabaseAccessConfiguration;
// import org.apache.shardingsphere.underlying.common.database.type.dialect.MySQLDatabaseType;
// import org.apache.shardingsphere.underlying.common.metadata.datasource.DataSourceMetas;
// import org.apache.shardingsphere.underlying.rewrite.SQLRewriteEntry;
// import org.apache.shardingsphere.underlying.rewrite.context.SQLRewriteContext;
// import org.apache.shardingsphere.underlying.rewrite.engine.SQLRewriteEngine;
// import org.apache.shardingsphere.underlying.rewrite.engine.SQLRewriteResult;
// import org.apache.shardingsphere.underlying.rewrite.engine.SQLRouteRewriteEngine;
// import org.apache.shardingsphere.underlying.route.DataNodeRouter;
// import org.apache.shardingsphere.underlying.route.context.RouteContext;
// import org.apache.shardingsphere.underlying.route.context.RouteUnit;
// import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
// import org.springframework.dao.DataAccessException;
// import org.springframework.r2dbc.core.DatabaseClient;
// import org.springframework.r2dbc.core.PreparedOperation;
// import org.springframework.r2dbc.core.binding.BindTarget;
// import reactor.core.publisher.Flux;
// import reactor.core.publisher.Mono;
//
// import java.util.Collection;
// import java.util.Collections;
// import java.util.HashMap;
// import java.util.List;
// import java.util.Map;
// import java.util.function.Function;
// import java.util.function.Supplier;
//
// /**
//  * @author LiYilin
//  * @since 2022/5/28 11:52 AM
//  **/
// @Slf4j
// public class TableShardingDatabaseClient implements DatabaseClient {
//     private final DatabaseClient delegation;
//     private final String dataNodeName;
//     private final SchemaMetaData schemaMetaData;
//     private final SQLParserEngine parserEngine;
//     private final DataNodeRouter dataNodeRouter;
//     private final SQLRewriteEntry sqlRewriteEntry;
//
//     public TableShardingDatabaseClient(DatabaseClient delegation, String dataNodeName, SchemaMetaData schemaMetaData,
//                                        SQLParserEngine parserEngine, DataNodeRouter dataNodeRouter,
//                                        SQLRewriteEntry sqlRewriteEntry) {
//         this.delegation = delegation;
//         this.dataNodeName = dataNodeName;
//         this.schemaMetaData = schemaMetaData;
//         this.parserEngine = parserEngine;
//         this.dataNodeRouter = dataNodeRouter;
//         this.sqlRewriteEntry = sqlRewriteEntry;
//     }
//
//     @Override
//     public ConnectionFactory getConnectionFactory() {
//         return delegation.getConnectionFactory();
//     }
//
//     @Override
//     public GenericExecuteSpec sql(String sql) {
//         return delegation.sql(resolveSql(sql));
//     }
//
//     @Override
//     public GenericExecuteSpec sql(Supplier<String> sqlSupplier) {
//         Supplier<String> wrapSqlSupplier;
//
//         if (sqlSupplier instanceof PreparedOperation<?>) {
//             // 兼容org.springframework.r2dbc.core.DefaultDatabaseClient
//             wrapSqlSupplier = wrapPreparedOperation((PreparedOperation<String>) sqlSupplier);
//         } else {
//             wrapSqlSupplier = () -> sqlSupplierGet(sqlSupplier);
//         }
//         return delegation.sql(wrapSqlSupplier);
//     }
//
//     /**
//      * 转换Logic SQL为Actual SQL
//      * @param sql
//      * @return
//      */
//     protected String resolveSql(String sql) {
//         SQLStatement sqlStatement = parserEngine.parse(sql, false);
//         List<Object> parameters = Collections.emptyList();
//         SQLStatementContext sqlStatementContext = SQLStatementContextFactory.newInstance(schemaMetaData, sql,
//                                                                                          parameters,
//                                                                                          sqlStatement);
//         RouteContext routeContext = dataNodeRouter.route(sql, parameters, false);
//         SQLRewriteContext sqlRewriteContext = sqlRewriteEntry.createSQLRewriteContext(sql, parameters,
//                                                                                       sqlStatementContext,
//                                                                                       routeContext);
//         Collection<String> sqls = routeContext.getRouteResult().getRouteUnits().isEmpty()
//                 ? rewrite(sqlRewriteContext)
//                 : rewrite(routeContext, sqlRewriteContext);
//         return sqls.stream().findFirst().orElse(null);
//     }
//
//     private Collection<String> rewrite(final SQLRewriteContext sqlRewriteContext) {
//         SQLRewriteResult sqlRewriteResult = new SQLRewriteEngine().rewrite(sqlRewriteContext);
//         return Lists.newArrayList(sqlRewriteResult.getSql());
//     }
//
//     private Collection<String> rewrite(final RouteContext routeContext, final SQLRewriteContext sqlRewriteContext) {
//         Collection<String> result = Lists.newArrayList();
//         for (Map.Entry<RouteUnit, SQLRewriteResult> entry : new SQLRouteRewriteEngine().rewrite(sqlRewriteContext
//                 , routeContext.getRouteResult()).entrySet()) {
//             result.add(entry.getValue().getSql());
//         }
//         return result;
//     }
//
//     private DataSourceMetas createDataSourceMetas(R2dbcProperties r2dbcProperties) {
//         Map<String, DatabaseAccessConfiguration> databaseAccessConfigurationMap = new HashMap<>();
//         String url = r2dbcProperties.getUrl().replace("r2dbcs", "jdbc")
//                 .replace("r2dbc", "jdbc");
//         DatabaseAccessConfiguration databaseAccessConfiguration =
//                 new DatabaseAccessConfiguration(url, r2dbcProperties.getUsername(),
//                                                 r2dbcProperties.getPassword());
//         databaseAccessConfigurationMap.put(dataNodeName, databaseAccessConfiguration);
//         return new DataSourceMetas(new MySQLDatabaseType(), databaseAccessConfigurationMap);
//     }
//
//     private PreparedOperation<String> wrapPreparedOperation(PreparedOperation<String> sqlSupplier) {
//         return new PreparedOperation<String>() {
//             @Override
//             public String getSource() {
//                 return sqlSupplier.getSource();
//             }
//
//             @Override
//             public void bindTo(BindTarget target) {
//                 sqlSupplier.bindTo(target);
//             }
//
//             @Override
//             public String toQuery() {
//                 return sqlSupplierGet(sqlSupplier);
//             }
//
//             @Override
//             public String get() {
//                 return sqlSupplier.get();
//             }
//         };
//     }
//
//     private String sqlSupplierGet(Supplier<String> sqlSupplier) {
//         String sql = sqlSupplier.get();
//         log.info("Logic SQL: {}", sql);
//         sql = resolveSql(sql);
//         log.info("Actual SQL: {}", sql);
//         return sql;
//     }
//
//     @Override
//     public <T> Mono<T> inConnection(Function<Connection, Mono<T>> action) throws DataAccessException {
//         return delegation.inConnection(action);
//     }
//
//     @Override
//     public <T> Flux<T> inConnectionMany(Function<Connection, Flux<T>> action) throws DataAccessException {
//         return delegation.inConnectionMany(action);
//     }
// }
